// $Id: iterator.cpp 2 2009-09-02 08:47:42Z joseph2002 $
/*
 * Copyright 2009 Joseph Wu
 * Distributed under the Boost Software License, Version 1.0.
 * (See accompanying file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt)
 *
 * For more information, see http://xpcomplus.sourceforge.net
 */

#include <algorithm>
#include <boost/test/unit_test.hpp>
#include <boost/test/test_case_template.hpp>
#include <xpcom/array.hpp>
#include <xpcom/ptr_array.hpp>
#include <xpcom/uuid.hpp>
#include "def.hpp"
#include "interface_def.hpp"

BOOST_AUTO_TEST_CASE(test_iterator_uint32)
{
    using namespace xpcom;
    typedef array<uint32_t> Array;

    Array a1(5), a2(5);
    BOOST_CHECK(a1.begin() == a1.begin());
    BOOST_CHECK(a1.end() == a1.end());
    BOOST_CHECK(a1.rbegin() == a1.rbegin());
    BOOST_CHECK(a1.rend() == a1.rend());

    uint32_t n = 10;
    for (Array::iterator iter = a1.begin(); iter != a1.end(); ++iter) {
        *iter = n;
        n += 10;
    }

    n = 10;
    for (Array::const_iterator iter = a1.begin(); iter != a1.end(); ++iter) {
        BOOST_CHECK(*iter == n);
        n += 10;
    }

    n = 50;
    for (Array::const_iterator iter = a1.end() - 1; iter != a1.begin(); --iter) {
        BOOST_CHECK(*iter == n);
        n -= 10;
    }

    n = 50;
    for (Array::const_reverse_iterator iter = a1.rbegin(), end = a1.rend(); iter != end; ++iter) {
        BOOST_CHECK(*iter == n);
        n -= 10;
    }

    Array::iterator i = a1.begin();
    i += 3;
    BOOST_CHECK(*i == 40);
    i = i - 2;
    BOOST_CHECK(*i == 20);
    *i = 200;
    BOOST_CHECK(*i == 200);

    Array::const_iterator ci = a1.begin() + 1;
    BOOST_CHECK(*ci == 200);

    *i = 20;

    std::reverse(a1.begin(), a1.end());

    n = 10;
    for (Array::const_reverse_iterator iter = a1.rbegin(), end = a1.rend(); iter != end; ++iter) {
        BOOST_CHECK(*iter == n);
        n += 10;
    }

    std::sort(a1.begin(), a1.end());

    n = 10;
    for (Array::const_iterator iter = a1.begin(); iter != a1.end(); ++iter) {
        BOOST_CHECK(*iter == n);
        n += 10;
    }

    // Compare
    BOOST_CHECK(a1 <= a1);
    BOOST_CHECK(a1 >= a1);

    n = 10;
    for (Array::iterator iter = a2.begin(); iter != a2.end(); ++iter) {
        *iter = n;
        n += 10;
    }
    BOOST_CHECK(a1 <= a2);

    a2.resize(4);
    BOOST_CHECK(a1 > a2);
    BOOST_CHECK(a2 < a1);
}

BOOST_AUTO_TEST_CASE(test_iterator_bool)
{
    using namespace xpcom;
    typedef array<bool> Array;

    Array a1(4);

    bool n = false;
    for (Array::iterator iter = a1.begin(); iter != a1.end(); ++iter) {
        *iter = n;
        n = !n;
    }

    n = false;
    for (Array::const_iterator iter = a1.begin(); iter != a1.end(); ++iter) {
        BOOST_CHECK(*iter == n);
        n = !n;
    }

    std::reverse(a1.begin(), a1.end());

    n = false;
    for (Array::reverse_iterator iter = a1.rbegin(); iter != a1.rend(); ++iter) {
        BOOST_CHECK(*iter == n);
        n = !n;
    }
}

BOOST_AUTO_TEST_CASE(test_iterator_pstring)
{
    using namespace xpcom;
    typedef array<char*> Array;

    Array a1(4);
    a1[0] = "test0";
    a1[1] = "test1";
    //a1[2] = "test2";
    a1[3] = "test3";

    Array::const_iterator ci = a1.begin();
    BOOST_CHECK(*ci++ == "test0");
    BOOST_CHECK(*ci++ == "test1");
    BOOST_CHECK(*ci++ == 0/*"test2"*/);
    BOOST_CHECK(*ci++ == "test3");

    Array::iterator i = a1.begin();
    i += 2;
    BOOST_CHECK(*i == 0);
    *i = "test2";
    BOOST_CHECK(*i == "test2");

    std::reverse(a1.begin(), a1.end());
    Array::const_reverse_iterator ri = a1.rbegin();
    BOOST_CHECK(*ri++ == "test0");
    BOOST_CHECK(*ri++ == "test1");
    BOOST_CHECK(*ri++ == "test2");
    BOOST_CHECK(*ri++ == "test3");
}

BOOST_AUTO_TEST_CASE(test_iterator_uuid)
{
    using namespace xpcom;
    typedef array<uuid> Array;

    uuid id1 = { 0x64D47515, 0xADE3, 0x45d1, { 0xAB, 0xAE, 0x3A, 0x93, 0x62, 0xC5, 0xA5, 0x51 } };
    uuid id2 = { 0x64D47516, 0xADE3, 0x45d1, { 0xAB, 0xAE, 0x3A, 0x93, 0x62, 0xC5, 0xA5, 0x51 } };
    uuid id3 = { 0x64D47517, 0xADE3, 0x45d1, { 0xAB, 0xAE, 0x3A, 0x93, 0x62, 0xC5, 0xA5, 0x51 } };
    uuid id4 = { 0x64D47518, 0xADE3, 0x45d1, { 0xAB, 0xAE, 0x3A, 0x93, 0x62, 0xC5, 0xA5, 0x51 } };

    Array a1(4);
    a1[0] = id1;
    a1[1] = id2;
    //a1[2] = id3;
    a1[3] = id4;

    Array::const_iterator ci = a1.begin();
    BOOST_CHECK(*ci++ == id1);
    BOOST_CHECK(*ci++ == id2);
    BOOST_CHECK(*ci++ == uuid::nil/*id3*/);
    BOOST_CHECK(*ci++ == id4);

    Array::iterator i = a1.begin();
    i += 2;
    BOOST_CHECK(i->is_nil());
    *i = id3;
    BOOST_CHECK(*i == id3);

    std::reverse(a1.begin(), a1.end());
    Array::const_reverse_iterator ri = a1.rbegin();
    BOOST_CHECK(*ri++ == id1);
    BOOST_CHECK(*ri++ == id2);
    BOOST_CHECK(*ri++ == id3);
    BOOST_CHECK(*ri++ == id4);
}

BOOST_AUTO_TEST_CASE(test_iterator_ptr)
{
    using namespace xpcom;
    typedef ptr_array<IBase> Array;

    X_count_guard g;
    X_count(0);
    {
        Array a1(4);
        for (int i = 0; i < 4; ++i) {
            if (i != 2)
                a1[i].reset(X_make_instance());
        }
        a1[0]->setN(0);
        a1[1]->setN(1);
        //a1[2]->setN(2);
        a1[3]->setN(3);
        BOOST_CHECK_EQUAL(X_count(), 3);

        Array::const_iterator ci = a1.begin();
        BOOST_CHECK((*ci++)->getN() == 0);
        BOOST_CHECK((*ci++)->getN() == 1);
        BOOST_CHECK(!(*ci++)/*->getN() == 2*/);
        BOOST_CHECK((*ci++)->getN() == 3);

        Array::iterator ii = a1.begin();
        ii += 2;
        BOOST_CHECK(!(*ii));
        (*ii).reset(X_make_instance());
        (*ii)->setN(2);
        BOOST_CHECK((*ii)->getN() == 2);

        BOOST_CHECK_EQUAL(X_count(), 4);

        std::reverse(a1.begin(), a1.end());
        Array::const_reverse_iterator ri = a1.rbegin();
        BOOST_CHECK((*ri++)->getN() == 0);
        BOOST_CHECK((*ri++)->getN() == 1);
        BOOST_CHECK((*ri++)->getN() == 2);
        BOOST_CHECK((*ri++)->getN() == 3);

        BOOST_CHECK_EQUAL(X_count(), 4);
    }
    BOOST_CHECK_EQUAL(X_count(), 0);
}
